package indi.zhifa.engine.bailan6.common.enumMemo.service.impl;

import cn.hutool.core.util.ClassUtil;

import indi.zhifa.engine.bailan6.common.enumMemo.entity.EnumItemVo;
import indi.zhifa.engine.bailan6.common.enumMemo.entity.EnumNodeVo;
import indi.zhifa.engine.bailan6.common.enumMemo.entity.EnumVo;
import indi.zhifa.engine.bailan6.common.enumMemo.entity.IEnumVo;
import indi.zhifa.engine.bailan6.common.enumMemo.entity.ann.EnumDesc;
import indi.zhifa.engine.bailan6.common.enumMemo.entity.ann.EnumExtItem;
import indi.zhifa.engine.bailan6.common.enumMemo.service.IEnumMemoService;
import indi.zhifa.engine.bailan6.common.web.entity.exception.ServiceException;
import lombok.Data;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

@Service
public class EnumMemoServiceImpl implements IEnumMemoService {

    public Map<String, EnumVo> enumsByName;
    public Map<Class, EnumNodeVo> enumMemoByClas;

    public EnumMemoServiceImpl(){
        enumsByName = new HashMap<String, EnumVo>();
        enumMemoByClas = new HashMap<Class, EnumNodeVo>();
    }

    @Data
    class ExtFieldInfo{
        String key;
        EnumExtItem enumExtItem;
        Field field;
    }

    @Override
    public void initEnum(String[] pEnumBasePackages) throws InvocationTargetException, IllegalAccessException {
        List<Class<?>> classes = new ArrayList<>();
        for(String path : pEnumBasePackages){
            Set<Class<?>> cls = ClassUtil.scanPackage(path);
            classes.addAll(cls);
        }
        for(Class cls : classes){
            if(!ClassUtil.isEnum(cls)){
                continue;
            }
            EnumDesc enumDesc = AnnotationUtils.findAnnotation(cls, EnumDesc.class);
            if(null == enumDesc){
                continue;
            }
            EnumVo enumDto = new EnumVo();
            String enumCode = cls.getSimpleName();
            enumDto.setCode(enumCode);
            enumDto.setName(enumDesc.name());
            enumDto.setDescription(enumDesc.desc());
            enumDto.setDefaultIdx(enumDesc.defaultIdx());
            enumDto.setDefaultItem(enumDesc.defaultItem());
            Object[] constants = cls.getEnumConstants();
            Method codeMethod = ClassUtil.getDeclaredMethod(cls, "getCode");
            Method nameMethod = ClassUtil.getDeclaredMethod(cls, "getName");
            Method descMethod = ClassUtil.getDeclaredMethod(cls, "getDescription");

            Field[] fields = cls.getDeclaredFields();
            Map<String,ExtFieldInfo> extEnumItemFieldMap = new HashMap<>();
            for(Field field : fields){
                EnumExtItem enumExtItem = field.getAnnotation(EnumExtItem.class);
                if(null == enumExtItem){
                    continue;
                }
                ExtFieldInfo extFieldInfo = new ExtFieldInfo();
                String key = enumExtItem.name();
                if(!StringUtils.hasText(key)){
                    key = field.getName();
                }
                extFieldInfo.setKey(key);
                extFieldInfo.setEnumExtItem(enumExtItem);
                extFieldInfo.setField(field);
                extEnumItemFieldMap.put(field.getName(),extFieldInfo);
            }


            List<EnumItemVo> enumItemDtoList = new ArrayList<>();
            for(Object obj : constants){
                String enumName = obj.toString();
                if(enumName.equals("DEFAULT")){
                    continue;
                }
                EnumItemVo enumItemDto = new EnumItemVo();
                enumItemDto.setName(enumName);
                if(null == codeMethod){
                    continue;
                }
                Object code = codeMethod.invoke(obj);
                String name = null;
                if(null != nameMethod){
                    name = (String)nameMethod.invoke(obj);
                }
                String desc = null;
                if(null != descMethod){
                    desc = (String)descMethod.invoke(obj);
                }
                enumItemDto.setCode(code);
                enumItemDto.setCname(name);
                enumItemDto.setDescription(desc);

                Map<String,Object> userData = new HashMap<>();
                for(Map.Entry<String,ExtFieldInfo> entry : extEnumItemFieldMap.entrySet()){
                    Field field = entry.getValue().getField();
                    boolean accessible = field.isAccessible();
                    field.setAccessible(true);
                    Object extVal = field.get(obj);
                    field.setAccessible(accessible);
                    userData.put(entry.getValue().getKey(),extVal);
                }
                if(userData.size()>0){
                    enumItemDto.setUserData(userData);
                }

                enumItemDtoList.add(enumItemDto);


            }
            enumDto.setItems(enumItemDtoList.toArray(new EnumItemVo[0]));
            enumsByName.put(enumDto.getCode(),enumDto);
            EnumNodeVo enumNodeDto = new EnumNodeVo(enumDto);
            enumMemoByClas.put(cls,enumNodeDto);

        }
    }

    @Override
    public IEnumVo findByName(String pName){
        EnumVo enumDto = enumsByName.get(pName);
        if(null == enumDto){
            throw new ServiceException("没有找到枚举名为"+pName+"的枚举");
        }
        return enumDto;
    }

    @Override
    public EnumNodeVo findByClass(Class pCls) {
        EnumNodeVo enumNodeDto = enumMemoByClas.get(pCls);
        return enumNodeDto;
    }

    @Override
    public EnumVo[] list() {
        EnumVo[] enumDtoArray = enumsByName.values().toArray(new EnumVo[0]);
        return enumDtoArray;
    }

    @Override
    public void addEnumDto(EnumVo pEnumDto) {
        if(!enumsByName.containsKey(pEnumDto.getCode())){
            enumsByName.put(pEnumDto.getCode(),pEnumDto);
        }
    }

}
